﻿///////////////////////////////////////////////////////
//sonystone 2002.4.3
///////////////////////////////////////////////////////
// TxtRichEdit.cpp : implementation file
//

#include "pch.h"
#include "LogRichEdit.h"
#include "FwbLog.h"
#include "WinLog.h"
#include "Database.h"
#include "TxtUtils.h"
#include "LangUtils.h"

#define CPP CLogRichEdit::

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

LOG_VIEW_INFO* mLVI = NULL;
void LogViewOpenClose(CLogRichEdit* edit, CStatusBarCtrl* bar, bool close) {	// 打开/关闭显示日志及状态栏
	if (!close) {
		if (mLVI == NULL) {
			mLVI = (LOG_VIEW_INFO*)malloc(sizeof(LOG_VIEW_INFO));
			if (mLVI == NULL) {
				return;
			}
			memset(mLVI, 0, sizeof(LOG_VIEW_INFO));
			FifoInit((FWB_FIFO*)mLVI->BarFifo, BAR_FIFO_ITEM, BAR_FIFO_POOL, 0);
			InitializeCriticalSection(&mLVI->CsLock);				// 初始化线程锁
		}
		if (edit != NULL) {
			mLVI->RichEdit = edit;
			mLVI->FileLength = 0;
			mLVI->FileOffset = 0;
//			mLVI->AddNumber = 0;
			mLVI->SaveOffset = 0;
	//		mLVI->SaveNumber = 0;
//			mLVI->SaveBytes = 0;
			mLVI->PlayCount = 0;							// 在显示期间的总行数
			mLVI->ShowCount = 0;							// 显示的行数
			mLVI->PauseCount = 0;							// 在暂停期间的行数
			mLVI->PauseOffset = 0;							// 在暂停期间的文件偏移量
			mLVI->FileOffset = 0;							// 在显示期间的文件偏移量



			mLVI->IsPause = false;
			mLVI->IsFocus = false;
			mLVI->IsChange = false;
			mLVI->IsKeyDown = false;
			mLVI->AppIdx = DbsGetMemValue(TEXT_LOG_APP_SELECT);		// 应用名索引 选择的LOG文件用于显示
			if (mLVI->AppIdx >= LOG_FILE_MAX) {				// 应用名索引 超出了
				mLVI->AppIdx = 0;
			}
		}
		if (bar != NULL) {
			mLVI->StatusBar = bar;
			mLVI->BarPosition = -1;
		}
	}else {
		if (edit != NULL) {
			mLVI->RichEdit = NULL;
		}else if (mLVI != NULL){
			DeleteCriticalSection(&mLVI->CsLock);					// 结束清除线程锁
			free(mLVI);
			mLVI = NULL;
		}
	}
}
void LogViewEditClearSave() {
	if (mLVI != NULL) {
		uint32_t counter = DbsGetMemValue(TEXT_LOG_COUNTER);
		if (counter < 11) {
			counter = 11;
		}
		char outPath[MAX_PATH];
		LogGetFilePath(outPath, mLVI->AppIdx, counter);	// 通过应用名索引 获取日志文件路程
		char srcPath[MAX_PATH];
		LogGetFilePath(srcPath, mLVI->AppIdx, 0);		// 通过应用名索引 获取日志文件路程
		BOOL result = MoveFileExA(srcPath, outPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
		if (result) {
			DbsSetMemValue(TEXT_LOG_COUNTER, counter + 1);
		}
		LogViewEditRestart();
	}
}
bool LogViewEditPause(bool change) {						// 暂停显示日志 change为true才改变
	if (mLVI != NULL) {
		if (change) {
			mLVI->IsPause = !mLVI->IsPause;
			LogViewEditFocus(mLVI->IsPause);
		}
		return mLVI->IsPause;
	}
	return false;
}
void LogViewEditFocus(bool focus) {							// 窗口获取/失去焦点
	if (mLVI != NULL) {
		mLVI->IsFocus = focus;								// 窗口获取到焦点，暂停显示
		if (focus) {										// 窗口获取到焦点，暂停显示
			mLVI->SaveOffset = mLVI->FileOffset;
		}else {												// 窗口失去焦点，正常显示
			mLVI->FileOffset = mLVI->SaveOffset;
//			FLOGS(BAR_LOG_INDEX, "已取消暂停");
		}
		mLVI->PauseCount = 0;								// 在暂停期间的行数
	}
}
void LogViewKeyDown(bool keyDown) {							// 窗口获取/失去焦点
	if (mLVI != NULL) {
		mLVI->IsKeyDown = keyDown;								// 窗口获取到焦点，暂停显示
	}
}
void LogViewEditRestart() {									// 重新读取日志
	if (mLVI != NULL && mLVI->RichEdit != NULL) {
		mLVI->FileOffset = 0;
		mLVI->FileLength = 0;
		mLVI->PauseCount = 0;								// 在暂停期间的行数
		mLVI->PauseOffset = 0;								// 在暂停期间的文件偏移量
		mLVI->RichEdit->Reset();
		mLVI->IsChange = true;								// 假装有数据更新了
		mLVI->RestrtSkipPause = true;						// 在暂停时重新调用LogViewEditRestart后需要先显示
	}
}
void LogViewEditCallback() {								// 日志更改通知的回调函数
	if (mLVI != NULL) {
		mLVI->IsChange = true;
	}
}
void LogViewBarCallback(int position, char* text, char* source) {	// 状态栏更改通知的回调函数 text为已经编码的字符
	if (mLVI != NULL) {
		if (position < BAR_ROW_FIXER) {						// 状态栏列及FIXER日志显示缓冲
			if (position == BAR_ROW_QTY) {
				TxtStrCpy(mLVI->BarFixer, sizeof(mLVI->BarFixer), source);
			}
			TxtStrCpy(mLVI->BarText[position] + 1, sizeof(mLVI->BarText[position]), text);
			mLVI->InBar[position] = true;					// 前面加一个空格	
		}else {
			char bytes[1024];
			bytes[0] = (char)((position & 0x0f) + '0');		// 显示的位置
			bytes[1] = (char)(((position >> 4) / 1000) + '0');	// 显示需要停留的秒
			int length = TxtStrCpy(bytes + 2, 128, text);
			FifoSetIn((FWB_FIFO*)mLVI->BarFifo, bytes, 2 + length);		//
		}
	}
}
static void editViewAdd(char* byte512, uint32_t length, char* source, uint32_t* offset) {
	int tLen = length - *offset;
	if (tLen >= 0x2000) {
		tLen = 0x2000 - 1;
	}
	char text[0x2000];
	if (LogFileRead(byte512, text, tLen + 1, *offset, true) > 0) {
		int addr = -1;
		int counter = 0;
		for (int index = 1; index < tLen; index++) {			// 如果key='\r'则扩展为以'\r''\n'为换行。
			if (text[index - 1] == '\r' && text[index] == '\n') {
				if (++counter > 512 / sizeof(size_t)) {
					break;
				}
				addr = index + 1;
			}
		}
		if (addr > 0) {										// 找到
			*offset += addr;
			addr = TxtKeyPreProcess(text, addr, '\r');		// 预处理以符合TxtKeyArray的要求
			int qty = TxtKeyArray((size_t*)byte512, text, addr, '\r', 1);
			char** lines = (char**)byte512;
			bool show = true;
			if (mLVI->IsFocus) {							// 暂停时
				show = mLVI->RestrtSkipPause;				// 重新调用LogViewEditRestart后需要先显示
			}
			if (show && mLVI->RichEdit != NULL) {			// 窗口失去焦点，正常显示 未暂停
				CLogRichEdit* edit = mLVI->RichEdit;
				for (int index = 0; index < qty; index++) {
					edit->AddLine(NULL, lines[index], source);
				}

				//edit->ReplaceSel((LPCTSTR)edit->mRER->FootTxt);
				mLVI->PlayCount += qty;						// 在显示期间的总行数
			}else {
				mLVI->PauseCount += qty;					// 在暂停期间的行数
			}
		}
	}
}

void LogViewOnTimer(char* source) {							// 日志及状态栏定时显示
	if (mLVI != NULL) {
		if (mLVI->IsChange) {
			for (int counter = 0; counter < 21; counter++) {	// 主要是限制一次读太多内容导致用户感觉卡住了
				char bytes[512];
				LogGetFilePath(bytes, mLVI->AppIdx, 0);		// 通过应用名索引 获取日志文件路程
				uint32_t length = (int)LogFileLength(bytes);
				uint32_t* offset = mLVI->IsFocus ? &mLVI->PauseOffset : &mLVI->FileOffset;
				if (length > *offset) {
					editViewAdd(bytes, length, source, offset);
				}else {
					mLVI->IsChange = false;
					mLVI->RestrtSkipPause = false;			// 在暂停时重新调用LogViewEditRestart后需要先显示
				}
			}
		}
		if (mLVI->BarStay != 0) {
			if ((OsGetTick() - mLVI->BarTick) >= (uint32_t)(mLVI->BarStay * 1000)) {
				mLVI->BarStay = 0;
				mLVI->BarPosition = -1;
			}
		}
		if (mLVI->BarStay == 0) {
			if (FifoIsOut((FWB_FIFO*)mLVI->BarFifo)) {
				int length;
				char* logDat = (char*)FifoGetOut((FWB_FIFO*)mLVI->BarFifo, &length);
				if (logDat != NULL && logDat[0] >= '0' && length > 1) {
					char bytes[1024];
					mLVI->BarPosition = logDat[0] - '0';
					if (mLVI->BarPosition >= BAR_ROW_QTY) {
						mLVI->BarPosition = BAR_ROW_QTY - 1;
					}
					mLVI->BarStay = logDat[1] - '0';
					bytes[0] = ' ';							// 前面加一个空格				
					length -= 2;
					memcpy(bytes + 1, logDat + 2, length);
					bytes[1 + length] = 0;
					CString temp(bytes);
					mLVI->StatusBar->SetText(temp, mLVI->BarPosition, 0);			// SBT_POPOUT
					mLVI->BarTick = OsGetTick();		// 返回支持跨平台的毫秒tick;
				}
			}
		}		
		for (int index = 0; index < BAR_ROW_QTY; index++) {	// 状态栏日志显示
			if (mLVI->BarPosition != -1 && mLVI->BarPosition == index) {		// 如果position已经显示,本次不能做对应的位置了
				continue;
			}
			if (mLVI->InBar[index]) {
				mLVI->InBar[index] = false;
				mLVI->BarText[index][0] = ' ';				// 前面加一个空格
				CString temp(mLVI->BarText[index]);
				mLVI->StatusBar->SetText(temp, index, 0);	// SBT_POPOUT
			}
		}
	}
}
int LogViewComboText(void* combo, int curSel, char* separate){		// 生成逗号分隔Combo文字
	char bytes[0x1000];
	char* array[32];
	int length = TxtStrCpy(bytes, sizeof(bytes), separate);
	if (bytes[length - 1] == SEPARATOR_LOG) {
		bytes[length - 1] = 0;								// 去掉最后一个逗号
	}
	int qty = TxtKeyArray((size_t*)array, bytes, length, SEPARATOR_LOG, 1);
	((CComboBox*)combo)->ResetContent();
	if (qty > 0 && qty < 32) {
		for (int index = 0; index < qty; index++) {
			CString string(array[index]);
			((CComboBox*)combo)->AddString(string);
		}
		if (curSel >= qty) {
			curSel = 0;
		}
		((CComboBox*)combo)->SetCurSel(curSel);
	}
	return qty;
}

void LogViewSetAppIdx(int index) {							// 设置应用名索引
	if (mLVI != NULL) {
		mLVI->AppIdx = index;
	}
}	
int LogViewGetAppIdx() {									// 获取应用名索引
	return (mLVI != NULL) ? mLVI->AppIdx : 0;
}
int LogViewCpyNoCtrlText(char* text, char* source, int length) {	// 将文字之中可能存在的控制字符转换为空格
	for (int index = 0; index < length; index++) {
		if ((uint8_t)source[index] < 0x80) {				// 是ascii码
			if (source[index] <= ' ' && source[index] != '\n') {
				text[index] = ' ';
				continue;
			}
		}
		text[index] = source[index];
	}
	return length;
}
int LogViewGetColorKey(char* byte32, bool darkTheme, LOG_COLOR logc) {
	static const char* tabLevel[] = {
		"Error", "Warn", "Info", "Debug", "Buffer", "Data", "Keyword", "Lost",
		"More0", "More1", "More2", "More3",
	};
	return TxtFormat(byte32, 32, "%sTheme%s", darkTheme ? "Dark" : "Light", tabLevel[logc]);
}


/////////////////////////////////////////////////////////////////////////////
// CLogRichEdit

CLogRichEdit::CLogRichEdit(){
	mRER = (RICK_EDIT_RAM*)malloc(sizeof(RICK_EDIT_RAM));
	if (mRER != NULL) {
		memset(mRER, 0, sizeof(RICK_EDIT_RAM));
	}

}

CLogRichEdit::~CLogRichEdit(){
	//MLOGD("CLogRichEdit Close");
	if (mRER != NULL) {
		free(mRER);
		mRER = NULL;
	}
}


BEGIN_MESSAGE_MAP(CLogRichEdit, CRichEditCtrl)
	ON_WM_DROPFILES()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLogRichEdit message handlers



LOG_CONFIG* CLogRichEdit::SetConfig() {						// 配置，如果有改变返回true
	LOG_CONFIG* config = &mRER->Config;
	memset(config, 0, sizeof(LOG_CONFIG));
	config->DarkTheme = DbsGetMemValue(TEXT_DARK_THEME) > 0;	//  0=浅色主题配色 1=深色主题配色
	config->SaveBuffFile = DbsGetMemValue(TEXT_LOG_SAVE_BIN) > 0;	// 保存时独立保存Buffer日志为bin文件
	config->Separate = DbsGetMemValue(TEXT_LOG_SEPARATE);	// 间隔类型 "空格间隔,[]间隔,[]+空格间隔"
	config->DateFmt = DbsGetMemValue(TEXT_LOG_DATE_FMT);	// 日期格式 0=不显示日期 1=MM-DD 2=YY-MM-DD
	config->TimeFmt = DbsGetMemValue(TEXT_LOG_TIME_FMT);	// 时间格式 0=HH:MM:SS 1=HH:MM:SS.MS3 2=HH:MM:SS.MS3 TICK
	config->AppNameFmt = DbsGetMemValue(TEXT_LOG_APP_NAME);	// 显示应用名 0=不显示 1=无条件显示 2=不为'0'时显示
	config->TagFmt = DbsGetMemValue(TEXT_LOG_TAG);			// 显示标签 0=不显示 1=无条件显示 2=不为'0'时显示
	config->TidFmt = DbsGetMemValue(TEXT_LOG_TID);			// 显示线程标识符 0=不显示 1=无条件显示 2=不为'0'时显示
	config->TimeCount = DbsGetMemValue(TEXT_LOG_TIME_COUNT);	// 计时格式

	static const uint32_t tabLightColor[] = {
		RGB(255,9,9), RGB(205,25,165), RGB(174,40,34), RGB(95,55,130), RGB(25,95,35),
		RGB(32,35,215), RGB(1,23,178), RGB(56,155,22),		// LOG_COLOR_DATA LOG_COLOR_KEYWORD LOG_COLOR_LOST
		RGB(32,35,215), RGB(76,251,82), RGB(56,155,22), RGB(56,155,22),		// 扩展颜色0-3
	};
	static const uint32_t tabDarkColor[] = {
		RGB(255,2,3), RGB(215,165,185), RGB(195,155,130), RGB(114,140,234), RGB(160,180,220),
		RGB(96,108,239), RGB(230,256,238), RGB(56,255,82),	// LOG_COLOR_DATA LOG_COLOR_KEYWORD LOG_COLOR_LOST
		RGB(232,235,215), RGB(212,195,235), RGB(56,155,22), RGB(56,55,92),		// 扩展颜色0-3
	};
	char keys[32];
	for (int index = 0; index < LOG_COLOR_SIZE; index++) {				// 日志配色RGB[LEWIDB] + 数据 + 关键字 + LOST
		LogViewGetColorKey(keys, config->DarkTheme, (LOG_COLOR)index);
		COLORREF vaule = DbsGetMemRgb(keys);
		if (vaule == 0) {
			vaule = config->DarkTheme ? tabDarkColor[index] : tabLightColor[index];
			DbsSetMemRgb(keys, vaule);
		}
		config->LogColor[index] = vaule;									// 日志Error配色RGB	
	}
	mRER->Number = 0;
	config->FtLevel = LOG_LEVEL_B;

	PARAFORMAT2 pf;
	pf.cbSize = sizeof(PARAFORMAT2);						// 识别paraformat与paraformat2
	pf.dwMask = PFM_LINESPACING;
	pf.dyLineSpacing = LOG_FONT_SPACE;						// 行距在此设置
	pf.bLineSpacingRule = 4;
	SetParaFormat((PARAFORMAT2)pf);

	uint32_t dwEventMast = this->GetEventMask();
	dwEventMast = dwEventMast | ENM_LINK;
	this->SetEventMask(dwEventMast);
	//HWND hWnd = this->m_hWnd;
//uint32_t mask = ::SendMessage(hWnd, EM_GETEVENTMASK, 0, 0);
//mask = mask | ENM_LINK | ENM_MOUSEEVENTS | ENM_SCROLLEVENTS | ENM_KEYEVENTS;
//::SendMessage(hWnd, EM_SETEVENTMASK, 0, mask);
//::SendMessage(hWnd, EM_AUTOURLDETECT, true, 0);



	return config;
}
void CLogRichEdit::Reset() {
	memset(&mRER->LastLtc, 0, sizeof(mRER->LastLtc));
	if (mLVI != NULL) {
		mLVI->PlayCount = 0;									// 显示的行数
		mLVI->ShowCount = 0;									// 显示的行数
		mLVI->PauseCount = 0;									// 在暂停期间的行数
	}
	SetWindowText(L"");
	SetScrollPos(SB_VERT, 0);								// 取得垂直滚动条位置
	SCROLLINFO scrollInfo = { 0 };
	SetScrollInfo(SB_VERT, &scrollInfo);
}
void CLogRichEdit::SetFocusA(bool focus) {
	mRER->Focus = focus;
}
void CPP SetFilter(LOG_CONFIG* config, bool pause, int level) {
	if (config == NULL) {
		config = &mRER->Config;
	}
	config->FtPause = pause;
	if (!pause) {
		config->FtLevel = level;
	}
}
// appName应用名 exclude排除 contain包含 keyword 关键字
void CLogRichEdit::SetFilter(LOG_CONFIG* config) {
	if (config == NULL) {
		config = &mRER->Config;
	}
	char bytes[1024];
	char codes[1024];
	if (DbsGetMemValue(TEXT_LOG_ENA_EXCLUD)) {
		DbsGetMemText(bytes, sizeof(bytes), TEXT_LOG_TXT_EXCLUD);
		TxtDecode(codes, sizeof(codes), bytes);				// 将文字解码为缓冲
		TextToArray(&config->FtExclude, LOG_FT_TEXT_SIZE, codes);
	}else {
		TextToArray(&config->FtExclude, LOG_FT_TEXT_SIZE, NULL);
	}
	if (DbsGetMemValue(TEXT_LOG_ENA_APP_NAME)) {
		DbsGetMemText(bytes, sizeof(bytes), TEXT_LOG_TXT_APP_NAME);
		TxtDecode(codes, sizeof(codes), bytes);				// 将文字解码为缓冲
		TextToArray(&config->FtAppName, LOG_FT_TEXT_SIZE, codes);
	}else {
		TextToArray(&config->FtAppName, LOG_FT_TEXT_SIZE, NULL);
	}
	if (DbsGetMemValue(TEXT_LOG_ENA_TAG)) {
		DbsGetMemText(bytes, sizeof(bytes), TEXT_LOG_TXT_TAG);
		TxtDecode(codes, sizeof(codes), bytes);				// 将文字解码为缓冲
		TextToArray(&config->FtTag, LOG_FT_TEXT_SIZE, codes);
	}else {
		TextToArray(&config->FtTag, LOG_FT_TEXT_SIZE, NULL);
	}
	if (DbsGetMemValue(TEXT_LOG_ENA_CONTAIN)) {
		DbsGetMemText(bytes, sizeof(bytes), TEXT_LOG_TXT_CONTAIN);
		TxtDecode(codes, sizeof(codes), bytes);				// 将文字解码为缓冲
		TextToArray(&config->FtContain, LOG_FT_TEXT_SIZE, codes);
	}else {
		TextToArray(&config->FtContain, LOG_FT_TEXT_SIZE, NULL);
	}
	if (DbsGetMemValue(TEXT_LOG_ENA_KEYWORD)) {
		DbsGetMemText(bytes, sizeof(bytes), TEXT_LOG_TXT_KEYWORD);
		TxtDecode(codes, sizeof(codes), bytes);				// 将文字解码为缓冲
		TextToArray(&config->FtKeyword, LOG_FT_TEXT_SIZE, codes);
	}else {
		TextToArray(&config->FtKeyword, LOG_FT_TEXT_SIZE, NULL);
	}
}
void CLogRichEdit::MakeLogTime(LOG_TIME* logtim, char* date, char* time, char* us) {
	logtim->Year = 2000 + TxtToSpecInt(date + 0, 2);		// 年 0为2020年
	logtim->Month = TxtToSpecInt(date + 2, 2);				// 月
	logtim->Day = TxtToSpecInt(date + 4, 2);				// 日	
	logtim->Hour = TxtToSpecInt(time + 0, 2); 				// 时
	logtim->Minute = TxtToSpecInt(time + 2, 2); 			// 分
	logtim->Second = TxtToSpecInt(time + 4, 2); 			// 秒
	int value = TxtToInt(us);								// 文字转换到数值
	logtim->Millisec = value / 1000; 						// 毫秒 最大1000毫秒
	logtim->IsUs = 1;										// 指示Tick的单位为us，如果是us
	logtim->Tick = (uint16_t)(value & 0xffff);				// IsUs为ms时最大为10秒。为us时需要在结构后面加多一个字节组成24位的数据。
	uint8_t* high = (uint8_t*)logtim;
	high[sizeof(LOG_TIME)] = (uint8_t)(value >> 16);
}


// [0]Level [1]线程ID [2]日期 [3]时间 [4]微秒 [5]标签 [6]应用名 [7]来源 [8]日志缓冲 [9]日志文字
void CLogRichEdit::AddLine(LOG_ARGV* argv, LOG_CONFIG* config) {		// 增加一行Level,应用名,标签,显示线程标识符,时间,日志文字,日志缓冲
	LOG_FILTER* input = (LOG_FILTER*)mRER->LineText;
	if (config == NULL) {
		config = &mRER->Config;
	}

	//TxtDecode(mRER->LineText + 0x3000, 0x1000, argv->Array[LOGIDX_TEXT]);		// 将缓冲编码为文字，outSize为输出的长度可以为NULL
	char* text = argv->Array[LOGIDX_TEXT];
	int tlen = TxtStrLen(text);
	if (text[tlen - 1] == '\r') {
		text[tlen - 1] = 0;
	}
	input->LtText = mRER->LineText + 0x2000;
	input->LtLen = TxtUtf8Gbk(input->LtText, 0x1000, text);			// UTF8转换到GBK


	if (!config->FtPause) {									// 不是过滤器暂停
		if (config->FtAppName.Qty > 0) {						// 应用名过滤
			if (!IsKeywordMatch(config, LOG_FT_APP_NAME, &config->FtAppName, input)) {	// 检查应用名符合返回true
				return;
			}
		}
		if (config->FtTag.Qty > 0) {						// 标签过滤
			if (!IsKeywordMatch(config, LOG_FT_TAG, &config->FtTag, input)) {	// 检查标签符合返回true
				return;
			}
		}
		if (config->FtExclude.Qty > 0 && IsKeywordMatch(config, LOG_FT_LOG_TEXT, &config->FtExclude, input)) {	// 检查有关键词符合返回true
			return;
		}
		if (config->FtContain.Qty > 0 && !IsKeywordMatch(config, LOG_FT_LOG_TEXT, &config->FtContain, input)) {	// 检查有关键词符合返回true
			return;
		}
		LOG_FILTER* keyword = (config->FtKeyword.Qty > 0) ? &config->FtKeyword : NULL;
		if (keyword != NULL) {
			IsKeywordMatch(config, LOG_FT_KEWWORD, keyword, input);		// 检查有关键词符合返回true
		}
	}
	// 逗号分隔 [0]Level [1]应用名 [2]标签 [3]显示线程标识符 [4]时间 [5]日志文字 [6]日志缓冲
	// [0]Level [1]线程ID [2]日期 [3]时间 [4]微秒 [5]标签 [6]应用名 [7]来源 [8]日志缓冲 [9]日志文字


	input->Logc = LOG_COLOR_B;							// 'E', 'W', 'I', 'D', 'B', 'F', 'K', 'L',
	switch (argv->Array[LOGIDX_LEVEL][0]) {								// [1]Level
	case 'E': input->Logc = LOG_COLOR_E; break;
	case 'W': input->Logc = LOG_COLOR_W; break;
	case 'I': input->Logc = LOG_COLOR_I; break;
	case 'D': input->Logc = LOG_COLOR_D; break;
	case 'K': input->Logc = LOG_COLOR_KEYWORD; break;
	case 'L': input->Logc = LOG_COLOR_LOST; break;
	}
	if (!config->FtPause && input->Logc > config->FtLevel) {	// 如果来的level比设定的大，就不显示
		if (input->Logc != LOG_COLOR_LOST) {				// 总是显示LOG_COLOR_LOST
			return;
		}
	}

	char info[0x1000];										// 会将文字放在info后面
	int offset = 0;
	if (config->Separate) {									// 间隔类型 0=空格 1=[] 2=[]+空格
		info[offset++] = '[';
	}
	uint8_t logtim[16];
	MakeLogTime((LOG_TIME*)logtim, argv->Array[2], argv->Array[3], argv->Array[4]);
	offset += WinLogTimeFormat(info + offset, logtim, NULL, config->DateFmt, config->TimeFmt);
	info[offset++] = (config->Separate == 0) ? ' ' : ']';
	if (config->Separate == 2) {							// 间隔类型 0=空格 1=[] 2=[]+空格
		info[offset++] = ' ';
	}
	
	char* separate = (config->Separate == 0) ? "%s " : (config->Separate == 1) ? "[%s]" : "[%s] ";
	offset += snprintf(info + offset, 32, separate, argv->Array[0]);

	if (config->AppNameFmt > 0) {							// 显示应用名 1=无条件显示 2=满足过滤条件显示或不为'0'时显示
		if (config->AppNameFmt == 1 || argv->Array[6][0] != '0') {
			offset = AddName(info, offset, separate, input, argv->Array[6]);
		}
	}
	if (config->TagFmt > 0) {								// 显示标签 1=无条件显示 2=满足过滤条件显示或不为'0'时显示
		if (config->TagFmt == 1 || argv->Array[5][0] != '0') {
			offset = AddName(info, offset, separate, input, argv->Array[5]);
		}
	}
	if (config->TidFmt > 0) {									// 显示线程标识符 1=无条件显示 2=满足过滤条件显示或不为'0'时显示
		if (config->TidFmt == 1 || argv->Array[1][0] != '0') {
			offset = AddName(info, offset, separate, input, argv->Array[1]);
		}
	}

	CHARFORMAT normal;
	CharFormat(normal, config->LogColor[input->Logc]);
	AddStyleLine(normal, config, info, offset, input, (LOG_TIME*)logtim);		// 会将文字放在info后面
	int length = TxtStrLen(argv->Array[8]);	// [8]日志缓冲
	if (length > 1) {
		normal.crTextColor = config->LogColor[LOG_COLOR_DATA];			// 日志配色RGB[EWIDB] + 数据的颜色	
		AddStyleLine(normal, config, argv->Array[8], length, NULL, NULL);
	}
	if (!mRER->Focus) {
		PostMessage(WM_VSCROLL, SB_BOTTOM, 0);
	}
}
void CLogRichEdit::AddLine(LOG_CONFIG* config, char* text, char* source) {	// 增加一行 Level,应用名,标签,显示线程标识符,时间,日志文字,日志缓冲
	LOG_FILTER* input = (LOG_FILTER*)mRER->LineText;
	if (!TextToArray(input, 0x1000, text)) {
		return;
	}
	if (source != NULL) {
		if (input->Qty > 7) {								// [0]Level [1]应用名 [2]标签 [3]显示线程标识符 [4]时间 [5]日志文字 [6]日志缓冲 [7]输入源
			// 有设置输入源 就必须要输入源相同 
			if (strlen(input->Array[7]) > 0 && !TxtStrCmp(source, input->Array[7], 0)) {
				return;										// 输入源不相同
			}
		}
	}
	TxtDecode(mRER->LineText + 0x3000, 0x1000, input->Array[5]);		// 将缓冲编码为文字，outSize为输出的长度可以为NULL
	input->LtText = mRER->LineText + 0x2000;
	input->LtLen = TxtUtf8Gbk(input->LtText, 0x1000, mRER->LineText + 0x3000);			// UTF8转换到GBK
	if (config == NULL) {
		config = &mRER->Config;
	}
	if (!config->FtPause){									// 不是过滤器暂停
		if (config->FtAppName.Qty > 0){						// 应用名过滤
			if (!IsKeywordMatch(config, LOG_FT_APP_NAME, &config->FtAppName, input)) {	// 检查应用名符合返回true
				return;
			}
		}
		if (config->FtTag.Qty > 0) {						// 标签过滤
			if (!IsKeywordMatch(config, LOG_FT_TAG, &config->FtTag, input)) {	// 检查标签符合返回true
				return;
			}
		}
		if (config->FtExclude.Qty > 0 && IsKeywordMatch(config, LOG_FT_LOG_TEXT, &config->FtExclude, input)) {	// 检查有关键词符合返回true
			return;
		}
		if (config->FtContain.Qty > 0 && !IsKeywordMatch(config, LOG_FT_LOG_TEXT, &config->FtContain, input)) {	// 检查有关键词符合返回true
			return;
		}
		LOG_FILTER* keyword = (config->FtKeyword.Qty > 0) ? &config->FtKeyword : NULL;
		if (keyword != NULL){
			IsKeywordMatch(config, LOG_FT_KEWWORD, keyword, input);		// 检查有关键词符合返回true
		}
	}
	// 逗号分隔 [0]Level [1]应用名 [2]标签 [3]显示线程标识符 [4]时间 [5]日志文字 [6]日志缓冲
	input->Logc = LOG_COLOR_B;							// 'E', 'W', 'I', 'D', 'B', 'F', 'K', 'L',
	switch (input->Array[0][0]) {								// [1]Level
	case 'E': input->Logc = LOG_COLOR_E; break;
	case 'W': input->Logc = LOG_COLOR_W; break;
	case 'I': input->Logc = LOG_COLOR_I; break;
	case 'D': input->Logc = LOG_COLOR_D; break;
	case 'K': input->Logc = LOG_COLOR_KEYWORD; break;
	case 'L': input->Logc = LOG_COLOR_LOST; break;
	}
	if (!config->FtPause && input->Logc > config->FtLevel) {	// 如果来的level比设定的大，就不显示
		if (input->Logc != LOG_COLOR_LOST) {				// 总是显示LOG_COLOR_LOST
			return;
		}
	}

	char info[0x1000];										// 会将文字放在info后面
	int offset = 0;
	if (config->Separate) {									// 间隔类型 0=空格 1=[] 2=[]+空格
		info[offset++] = '[';
	}
	uint8_t logtim[16];
	offset += WinLogTimeFormat(info + offset, logtim, input->Array[4], config->DateFmt, config->TimeFmt);
	info[offset++] = (config->Separate == 0) ? ' ' : ']';
	if (config->Separate == 2) {							// 间隔类型 0=空格 1=[] 2=[]+空格
		info[offset++] = ' ';
	}
	
	char* separate = (config->Separate == 0) ? "%s " : (config->Separate == 1) ? "[%s]" : "[%s] ";
	offset += snprintf(info + offset, 32, separate, input->Array[0]);

	if (config->AppNameFmt > 0) {							// 显示应用名 1=无条件显示 2=满足过滤条件显示或不为'0'时显示
		if (config->AppNameFmt == 1 || input->Array[1][0] != '0') {
			offset = AddName(info, offset, separate, input, 1);
		}
	}
	if (config->TagFmt > 0) {								// 显示标签 1=无条件显示 2=满足过滤条件显示或不为'0'时显示
		if (config->TagFmt == 1 || input->Array[2][0] != '0') {
			offset = AddName(info, offset, separate, input, 2);
		}
	}
	if (config->TidFmt > 0) {									// 显示线程标识符 1=无条件显示 2=满足过滤条件显示或不为'0'时显示
		if (config->TidFmt == 1 || input->Array[3][0] != '0') {
			offset = AddName(info, offset, separate, input, 3);
		}
	}
	CHARFORMAT normal;
	CharFormat(normal, config->LogColor[input->Logc]);
	AddStyleLine(normal, config, info, offset, input, (LOG_TIME*)logtim);		// 会将文字放在info后面
	if (input->Qty > 6 && input->Length[6] > 0) {
		char bytes[0x800];
		char codes[0x400];
		int outSize = TxtDecode(codes, sizeof(codes), input->Array[6]);	// 将缓冲编码为文字，outSize为输出的长度可以为NULL
		if (outSize > 256) {
			outSize = 256;
		}
		offset = 0;
		for (int index = 0; index < outSize; index++) {
			bytes[offset++] = TxtHexAscii(codes[index] >> 4);
			bytes[offset++] = TxtHexAscii(codes[index]);
			if ((index % 8) == 7) {
				bytes[offset++] = ' ';
			}
		}
		normal.crTextColor = config->LogColor[LOG_COLOR_DATA];			// 日志配色RGB[EWIDB] + 数据的颜色	
		AddStyleLine(normal, config, bytes, offset, NULL, NULL);
	}
	if (!mRER->Focus) {
		PostMessage(WM_VSCROLL, SB_BOTTOM, 0);
	}
	++mLVI->ShowCount;
}
void CLogRichEdit::AddLine(LOG_COLOR logc, char* text, int length) {
	AddLine(logc, 0, text, length);
}
void CLogRichEdit::AddLine(LOG_COLOR logc, COLORREF color, char* text, int length) {
	int offset = TxtFindLeft(text, length, 'h');
	if (offset >= 0) {
		if (length == 0) {
			length = (int)strlen(text);
		}
		if ((offset + 12) < length) {
			bool find = false;
			if (TxtStrCmp(text + offset, "http://", 7)) {
				find = true;
			}else if (TxtStrCmp(text + offset, "https://", 8)) {
				find = true;
			}
			if (find) {
				//CHARFORMAT2 normal;
				//normal.cbSize = sizeof(CHARFORMAT2);
				//normal.dwMask = CFM_LINK;
				//normal.dwEffects = normal.dwEffects | CFE_LINK;

				//normal.crTextColor = color;
				//return;
			}
		}
	}
	CHARFORMAT2 normal;
	CharFormat(normal, logc < LOG_COLOR_SIZE ? mRER->Config.LogColor[logc] : color);
	normal.cbSize = sizeof(CHARFORMAT2);
	SetSel(-1, -1);
	WCHAR word[0x1000];
	TxtGbkUnicode((char*)word, sizeof(word), text);			// GBK转换到UNICODE
	SetSelectionCharFormat(normal);							// 设置颜色	
	ReplaceSel(word);
	++mLVI->ShowCount;
}
uint32_t CLogRichEdit::GetLineCountA(uint32_t* show, uint32_t* pause) {		// 获取显示的行数
	if (show != NULL) {
		*show = mLVI->ShowCount;
	}
	if (pause != NULL) {
		*pause = mLVI->PauseCount;							// 在暂停期间的行数
	}
	return mLVI->PlayCount;
}

bool CLogRichEdit::SaveLogFile(char* outPath, int number) {	// 保存日志文件，返回刚刚保存的文件名	
	WinLogCsLock();										// 设置等待线程锁
	char srcPath[MAX_PATH];
	char dstPath[MAX_PATH];
	LogGetFilePath(dstPath, number, GetFileCount());			// 获取日志文件路程
	LogGetFilePath(srcPath, number, 0);					// 获取日志文件路程
	BOOL result = MoveFileExA(srcPath, dstPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
	if (result) {
		Reset();
	}
	WinLogUnCsLock();											// 释放线程锁
	int length;
	char* text = (char*)LogFileLoad(dstPath, &length);
	if (text != NULL) {
		char** array = (char**)malloc(length/2);
		if (array != NULL) {
			int offset = TxtFindRight(dstPath, 0, '.');
			if (offset > 0) {
				dstPath[offset] = 0;						// 扩展名
			}
			TxtFormat(outPath, MAX_PATH, "%s.txt", dstPath);
			FILE* file;
			errno_t err = fopen_s(&file, (char const*)outPath, "wb+");
			if (file != NULL) {
				length = TxtKeyPreProcess(text, length, '\r');	// 预处理以符合TxtKeyArray的要求
				int qty = TxtKeyArray((size_t*)array, text, length, '\r', 1);
				for (int index = 0; index < qty; index++) {
					SaveLine(array[index], file, outPath, number);
				}
				fclose(file);
			}
			free(array);
		}
		free(text);
	}
	return result ? true : false;
}

void CLogRichEdit::SaveLine(char* text, FILE* file, char* outPath, int number) {
	char* array[128];
	char bytes[0x1000 * 2];									// GBK转换到UNICODE	要用多一倍的内存
	char codes[1024];
	int length = TxtStrLen(text);
	int qty = TxtFindQty(text, length, SEPARATOR_LOG);		// 返回spec的数量
	if (length > 0x1000 || qty < 5 || qty > 16) {
		return;
	}

	qty = TxtKeyArray((size_t*)array, text, length, SEPARATOR_LOG, 1);
	if (qty >= 6 && qty < 16) {								// 逗号分隔 [0]Level [1]应用名 [2]标签 [3]显示线程标识符 [4]时间 [5]日志文字 [6]日志缓冲
		// 日期格式 0=不显示日期 1=MM-DD 2=YY-MM-DD
		// 时间格式 0=HH:MM:SS.MS3 1=HH:MM:SS.MS3 TICK
		uint8_t logtim[16];
		int offset = WinLogTimeFormat(bytes, logtim, array[4], 2, 1);
		offset += snprintf(bytes + offset, 32, " %s ", array[0]);

		offset += snprintf(bytes + offset, 32, "%s ", array[1]);
		offset += snprintf(bytes + offset, 32, "%s ", array[2]);
		offset += snprintf(bytes + offset, 32, "%s ", array[3]);
		fwrite(bytes, 1, offset, file);
		int outSize = TxtDecode(codes, sizeof(codes), array[5]);	// 将缓冲编码为文字，outSize为输出的长度可以为NULL
		offset = TxtUtf8Gbk(bytes, sizeof(bytes), codes);			// UTF8转换到GBK
		if (qty > 6 && strlen(array[6]) > 0) {
			outSize = TxtDecode(codes, sizeof(codes), array[6]);	// 将缓冲编码为文字，outSize为输出的长度可以为NULL

			offset += snprintf(bytes + offset, 16, " %d[", outSize);
			for (int index = 0; index < outSize; index++) {
				bytes[offset++] = TxtHexAscii(codes[index] >> 4);
				bytes[offset++] = TxtHexAscii(codes[index]);
				if ((index % 8) == 7) {						// 每隔8个字节加一个空格
					bytes[offset++] = ' ';
				}
			}
			if (bytes[offset - 1] == ' ') {					// 最后一个刚好是空格
				offset -= 1;								// 去掉空格
			}
			bytes[offset++] = ']';
			if (mRER->Config.SaveBuffFile && array[0][0] == 'B') {		// 保存时独立保存Buffer日志为bin文件
				char dstPath[MAX_PATH];
				LogGetFilePath(dstPath, number, 0);		//GetFileCount());	// 获取日志文件路程
				int offset = TxtFindRight(dstPath, 0, '.');
				if (offset > 0) {
					TxtStrCpy(dstPath + offset, 16, "\\");
					OsPathMake(outPath);
				}
				char name[64];
				int nlen = (int)TxtDecode(name, sizeof(name), array[5]);
				int colon = TxtFindLeft(name, nlen, ':');	//  冒号
				bool onebin = false;
				if (colon > 0) {		// 有冒号而且后面跟随数值为偏移量，会合并所有相同名字的日志到一个文件
					nlen = LangRemoveBetweenSpace(name, nlen);
					colon = TxtFindLeft(name, nlen, ':');	//  冒号
					char* atxt = name + colon + 1;
					int alen = nlen - colon - 1;
					int isv = TxtIsValue(atxt, alen);
					if (isv > 0) {
						size_t addr = (isv == 2) ? TxtHexToValue(atxt + 2, alen - 2) : TxtToValue(atxt, alen);
						LangNameOnly(name, colon);					// 只保留字母及数字
						TxtFormat(outPath, MAX_PATH, "%s_%s.bin", dstPath, name);
						LogFileWrite(outPath, codes, outSize, addr);
						onebin = true;
					}
				}
				if (!onebin) {
					LangNameOnly(name, nlen);					// 只保留字母及数字

					//char name[32];
					//int counter = 0;
					//for (int index = 0; index < nlen; index++) {
					//	uint8_t tmp0 = codes[index];
					//	if ((tmp0 >= 'A' && tmp0 <= 'Z') || (tmp0 >= 'a' && tmp0 <= 'z') || (tmp0 >= '0' && tmp0 <= '9')) {
					//		name[counter] = tmp0;
					//		if (++counter >= (sizeof(name) - 1)) {
					//			break;
					//		}
					//	}
					//}
					//name[counter] = 0;
					TxtFormat(outPath, MAX_PATH, "%s_%s.bin", dstPath, name);
					LogFileWrite(outPath, codes, outSize, 0);
				}
			}
		}
		bytes[offset++] = '\r';
		bytes[offset++] = '\n';
		fwrite(bytes, 1, offset, file);
	}else {
		int test = 1;
	}
}

bool CLogRichEdit::CheckInput(LOG_CONFIG* config, LOG_FT_TYPE type, LOG_FILTER* filter, uint8_t* text, int length) {
	if (length > 1) {										// 至少两个字以上
		for (int index = 0; index < filter->Qty; index++) {	// 找阵列
			uint8_t* fTxt = (uint8_t*)filter->Array[index];
			int fLen = filter->Length[index];
			for (int counter = 0; counter < length; counter++) {	// 从输入之中找
				if (fTxt[0] == text[counter]) {				// 找到一个字相同后
					if (fLen <= (length - counter)) {		// 过滤器长度少于剩余的输入长度才有必要对比
						bool match = true;
						for (int position = 1; position < fLen; position++) {
							if (fTxt[position] != text[counter + position]) {	// 完全相同
								match = false;
								break;
							}
						}
						if (match) {
							return match;
						}
					}
				}
			}
		}
	}
	return false;
}
bool CLogRichEdit::IsKeywordMatch(LOG_CONFIG* config, LOG_FT_TYPE type, LOG_FILTER* filter, LOG_FILTER* input) {	// 检查有关键词符合返回true
	// [0]Level [1]应用名 [2]标签 [3]显示线程标识符 [4]时间 [5]日志文字 [6]日志缓冲
	if (type == LOG_FT_APP_NAME){							// 应用名过滤
		return CheckInput(config, type, filter, (uint8_t*)input->Array[1], input->Length[1]);	// [1]应用名
	}
	if (type == LOG_FT_TAG) {								// 标签过滤
		return CheckInput(config, type, filter, (uint8_t*)input->Array[2], input->Length[2]);		// [2]标签
	}
	if (type == LOG_FT_LOG_TEXT) {							// 日志文字过滤
		return CheckInput(config, type, filter, (uint8_t*)input->LtText, input->LtLen);		// [5]日志文字 解码后的gbk为LtText
	}
	if (input->LtLen > 1) {									// LOG_FT_KEWWORD 至少两个字以上
		int offset = 0;
		int addr = 0;
		int index = 0;
		for (; index < input->LtLen; index++) {				// 从输入之中找
			char* text = input->LtText + index;
			int length = input->LtLen - index;
			for (int counter = 0; counter < filter->Qty; counter++) {	// 找阵列
				if (length >= filter->Length[counter]) {
					if (TxtStrCmp(text, filter->Array[counter], filter->Length[counter])) {
						if (addr != index) {				// 有普通颜色的文字
							mRER->ColorAddr[offset] = addr;
							mRER->ColorLen[offset] = index - addr;
							mRER->ColorTyp[offset] = false;
							++offset;
						}
						mRER->ColorAddr[offset] = index;	// 有高亮加粗颜色的文字
						mRER->ColorLen[offset] = filter->Length[counter];
						mRER->ColorTyp[offset] = true;
						++offset;
						index += filter->Length[counter];
						addr = index;
					}
				}
			}
		}
		if (offset > 0 && addr != index) {								// 有普通颜色的文字
			mRER->ColorAddr[offset] = addr;
			mRER->ColorLen[offset] = index - addr;
			mRER->ColorTyp[offset] = false;
			++offset;
		}
		mRER->ColorTotal = offset;
	}
	return false;
}
bool CLogRichEdit::TextToArray(LOG_FILTER* filter, int SIZEOF, char* text) {
	if (text != NULL) {
		int length = TxtStrCpy(filter->Text, SIZEOF, text);
		filter->Qty = TxtKeyArray((size_t*)filter->Array, filter->Text, length, SEPARATOR_LOG, 1);
		if (filter->Qty > 0 && filter->Qty < 16) {
			for (int index = 0; index < filter->Qty; index++) {
				filter->Length[index] = (int)strlen(filter->Array[index]);
			}
			return true;
		}
	}
	filter->Qty = 0;
	return false;
}
int CLogRichEdit::ChangColor(int rgb, int up) {
	int cr = rgb & 0xff;
	int cg = (rgb >> 8) & 0xff;
	int cb = (rgb >> 16) & 0xff;
	cr += up;
	cg += up;
	cb += up;
	if (up > 0) {
		if (cr > 0xff) cr = 0xff;
		if (cg > 0xff) cg = 0xff;
		if (cb > 0xff) cb = 0xff;
	}
	else {
		if (cr < 0x00) cr = 0x00;
		if (cg < 0x00) cg = 0x00;
		if (cb < 0x00) cb = 0x00;
	}
	return cr | (cg << 8) | (cb << 16);
}
bool CLogRichEdit::IsAdjoin(int last, int now, int carry) {		// 看看时间是否相邻 carry为进位数值用于本次小于上次反向判断
	if (last == now) {
		return true;										// 相同
	}
	if (last > now) {										// 上次大于本次的
		now += carry;										// 进位
	}
	return (last + 1) == now;								// 相邻
}
uint32_t CLogRichEdit::GetLastTick(LOG_FILTER* input, LOG_TIME* last, LOG_TIME* logt) {
	uint32_t timLas;									// last
	uint32_t timNow;									// now
	timLas = (last->Second * 1000) + last->Millisec;	// 上次的毫秒
	if (!IsAdjoin(last->Minute, logt->Minute, 60)) {	// 分是否相等或相邻
		return 0xffffffff;								// 分不相邻 超过1分钟了	
	}
	if (!IsAdjoin(last->Hour, logt->Hour, 24)) {		// 小时是否相等或相邻
		return 0xffffffff;								// 小时不相邻 超过1分钟了	
	}
	if (last->Year == logt->Year && last->Month == logt->Month && last->Day == logt->Day) {		// 同一天
		timNow = (logt->Second * 1000) + logt->Millisec;	// 本次的毫秒
	}else {												// 不同一天
		// 日的进位有28 30 31，需要找出本月的最大日进位
		int carry = 31;
		if (!IsAdjoin(last->Day, logt->Day, carry)) {	// 日不相邻
			return 0xffffffff;							// 超过1分钟了	
		}
		if (!IsAdjoin(last->Month, logt->Month, 12)) {	// 月不相邻
			return 0xffffffff;							// 超过1分钟了	
		}
		if (!IsAdjoin(last->Year, logt->Year, 0)) {		// 年不相邻 年没有进位
			return 0xffffffff;							// 超过1分钟了	
		}
		timNow = (logt->Second * 1000) + logt->Millisec;	// 本次的毫秒
	}
	if (timNow < timLas) {								// 本次小于上次的
		timNow += 60 * 1000;
	}
	if ((timNow - timLas) < 10000) {						// 10秒之内，使用tick的计数
		uint32_t usLas = last->Tick;						// last
		uint32_t usNow = logt->Tick;						// now
		if (!last->IsUs) {									// 不是微秒
			usLas *= 1000;
		}else {												// 微秒
			usLas |= (((uint8_t*)last)[sizeof(LOG_TIME)] << 16);
		}
		if (!logt->IsUs) {									// 不是微秒
			usNow *= 1000;
		}else{												// 微秒
			usNow |= (((uint8_t*)logt)[sizeof(LOG_TIME)] << 16);
		}
		if (usNow >= usLas) {								// 正常的
			return usNow - usLas;
		}
		return (usNow + 10000000) - usLas;
	}
	return (timNow - timNow) * 1000;
}

int CLogRichEdit::AddName(char* info, int offset, char* separate, LOG_FILTER* input, int index) {
	char bytes[64];
	char gbk[64];
	TxtDecode(bytes, sizeof(bytes), input->Array[index]);	// 将缓冲编码为文字，outSize为输出的长度可以为NULL
	TxtUtf8Gbk(gbk, sizeof(bytes), bytes);					// UTF8转换到GBK
	offset += snprintf(info + offset, 32, separate, gbk);
	info[offset] = 0;
	return offset;
}
int CLogRichEdit::AddName(char* info, int offset, char* separate, LOG_FILTER* input, char* text) {
	char bytes[64];
	char gbk[64];
	TxtDecode(bytes, sizeof(bytes), text);	// 将缓冲编码为文字，outSize为输出的长度可以为NULL
	TxtUtf8Gbk(gbk, sizeof(bytes), bytes);					// UTF8转换到GBK
	offset += snprintf(info + offset, 32, separate, gbk);
	info[offset] = 0;
	return offset;
}
void CLogRichEdit::CharFormat(CHARFORMAT& normal, COLORREF color) {
	normal.cbSize = sizeof(CHARFORMAT);
	normal.dwEffects = 0;
	normal.yHeight = LOG_FONT_HEIGHT;						// 设置高度
	normal.dwMask = CFM_COLOR | CFM_SIZE | CFE_BOLD;		// 允许设置粗体
	normal.crTextColor = color;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 编辑文字输入及拖动文件 ////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// https://blog.csdn.net/dotphoenix/article/details/18601617?%3E
// CRichedit在结尾添加一行，同时设置字体，大小及颜色，并在添加后自动滚屏到末行
int CLogRichEdit::ShowLineEnd(char* text, int length, LOG_CONFIG* config, LOG_FILTER* input, LOG_TIME* logt, bool endOnly) {
	if (!endOnly) {
		if (config->Separate > 0) {							// 间隔类型 0=空格 1=[] 2=[]+空格
			text[length++] = ']';
		}
		if (config->TimeCount > 0 && input->Logc <= LOG_COLOR_DATA) {	// 计时格式
			if (config->Separate != 1) {					// 间隔类型 0=空格 1=[] 2=[]+空格
				text[length++] = ' ';
			}
			if (config->Separate == 0) {					// 间隔类型 0=空格 1=[] 2=[]+空格
				text[length++] = '[';
			}
			else {
				length += TxtStrCpy(text + length, 16, "【");
			}
			LOG_TIME_COUNT* match;
			if (config->TimeCount == 1) {					// 计时格式 0=不计时 1=不限 2=相同应用名 3=相同标签 4=相同线程标识符 5=相同输入源
				match = &mRER->LastLtc[0];
			}
			else {
				int index = 0;
				match = &mRER->LastLtc[index];
			}
			LOG_TIME* last = (LOG_TIME*)match->Time;
			if (last->Year) {										// 有上次的记录
				uint32_t us = GetLastTick(input, last, logt);
				uint32_t decimal;
				if (us < 1000) {
					length += TxtFormat(text + length, 16, "用时%dus", us);
				}
				else if (us < 1000000) {
					decimal = us % 1000;
					if (decimal == 0) {
						length += TxtFormat(text + length, 16, "用时%dms", us / 1000);
					}
					else {
						length += TxtFormat(text + length, 16, "用时%d.%dms", us / 1000, decimal);
					}
				}
				else if (us < 10000000) {
					decimal = (us % 1000000) / 1000;
					if (decimal == 0) {
						length += TxtFormat(text + length, 16, "用时%d秒", us / 1000000);
					}
					else {
						length += TxtFormat(text + length, 16, "用时%d.%d秒", us / 1000000, decimal);
					}
				}
				else {
					length += TxtStrCpy(text + length, 16, "超过1分钟");
				}
			}
			else {
				length += TxtStrCpy(text + length, 16, "计时开始");
			}
			memcpy(last, logt, sizeof(LOG_TIME) + 1);
			if (config->TimeCount > 1) {
			}
			if (config->Separate == 0) {					// 间隔类型 0=空格 1=[] 2=[]+空格
				text[length++] = ']';
			}
			else {
				length += TxtStrCpy(text + length, 16, "】");
			}
		}
	}
	LogViewCpyNoCtrlText(text, text, length);		// 将文字之中可能存在的控制字符转换为空格
	text[length++] = '\r';
	text[length++] = '\n';
	text[length] = 0;
	return length;
}

void CLogRichEdit::AddStyleLine(CHARFORMAT& normal, LOG_CONFIG* config, char* info, int length, LOG_FILTER* input, LOG_TIME* logt) {
	SetSel(-1, -1);
	WCHAR word[0x1000];
	if (input != NULL) {
		if (mRER->ColorTotal > 1) {								// 当前行有多颜色高亮显示	
			SetSelectionCharFormat(normal);						// 设置颜色	
			if (config->Separate > 0) {							// 间隔类型 0=空格 1=[] 2=[]+空格
				info[length++] = '[';
			}
			info[length] = 0;
			LogViewCpyNoCtrlText(info, info, length);			// 将文字之中可能存在的控制字符转换为空格
			TxtGbkUnicode((char*)word, sizeof(word), info);		// GBK转换到UNICODE
			ReplaceSel((LPCTSTR)word);

			CHARFORMAT highL;
			highL.cbSize = sizeof(CHARFORMAT);
			highL.dwEffects = CFE_BOLD;						//  | CFE_ITALIC | CFE_UNDERLINE
			int up = (config->DarkTheme) ? 60 : -60;
			highL.crTextColor = config->LogColor[LOG_COLOR_KEYWORD];
			highL.yHeight = LOG_FONT_HEIGHT + 60;			// 设置高度
			highL.dwMask = CFM_COLOR | CFM_SIZE | CFE_BOLD;	// 允许设置粗体

			char bytes[0x1000];
			for (int index = 0; index < mRER->ColorTotal; index++) {
				memcpy(bytes, input->LtText + mRER->ColorAddr[index], mRER->ColorLen[index]);
				bytes[mRER->ColorLen[index]] = 0;
				TxtGbkUnicode((char*)word, sizeof(word), bytes);	// GBK转换到UNICODE

				SetSelectionCharFormat(!mRER->ColorTyp[index] ? normal : highL);	// 设置颜色	
				ReplaceSel((LPCTSTR)word);
			}
			length = 0;
			ShowLineEnd(bytes, length, config, input, logt, false);

			SetSelectionCharFormat(normal);
			TxtGbkUnicode((char*)word, sizeof(word), bytes);	// GBK转换到UNICODE
			ReplaceSel(word);
			return;
		}
		if (config->Separate == 0) {						// 间隔类型 0=空格 1=[] 2=[]+空格
			length += LogRecordTxtDec(info + length, 0x1000 - 0x100, input->LtText, 0);
		}
		else {
			info[length++] = '[';
			length += LogRecordTxtDec(info + length, 0x1000 - 0x100, input->LtText, 0);
		}
		ShowLineEnd(info, length, config, input, logt, false);
	}
	else {
		ShowLineEnd(info, length, config, input, logt, true);
	}
	SetSelectionCharFormat(normal);							// 设置颜色	
	TxtGbkUnicode((char*)word, sizeof(word), info);			// GBK转换到UNICODE
	ReplaceSel(word);
}


void CLogRichEdit::OnDropFiles(HDROP hDropInfo) {			// 拖动文件函数	
	int qty = DragQueryFileA(hDropInfo, 0xffffffff, NULL, 0);
	if (qty > 0) {
		char bytes[16 * MAX_PATH];
		char path[MAX_PATH];
		int offset = 0;
		for (int index = 0; index < qty; index++) {
			DragQueryFileA(hDropInfo, index, path, MAX_PATH);
			if (index > 0) {
				bytes[offset++] = SEPARATOR_LOG;
			}
			offset += TxtStrCpy(bytes + offset, sizeof(bytes) - offset, path);
		}
		if (mDropFilesCb != NULL) {
			mDropFilesCb(bytes, offset);
		}
	}
}
void CLogRichEdit::SetDropFilesCb(void(*dropFilesCb)(char* paths, int length)) {	// 设置拖动文件回调函数	
	mDropFilesCb = dropFilesCb;								// 拖动文件回调函数	
}




int CLogRichEdit::GetFileCount() {
	int counter = DbsGetMemValue(TEXT_LOG_COUNTER);
	if (counter < 11 || counter > 1000000) {
		counter = 11;
	}
	DbsSetMemValue(TEXT_LOG_COUNTER, counter + 1);
	return counter;
}

/*

 open the .RC file as text, change the class name of each rich edit control from  "RICHEDIT" to "RichEdit20a".
			Then replace the call to AfxInitRichEdit with AfxInitRichEdit2.

(27条消息) CRichEditCtrl的使用(很全面)_Fields_Of_Gold的博客-CSDN博客
https://blog.csdn.net/liu__ke/article/details/8735917
*/



int CPP GetNumVisibleLines() {
	CRect rect;
	long nFirstChar, nLastChar;
	long nFirstLine, nLastLine;

	// Get client rect of rich edit control
	GetClientRect(rect);

	// Get character index close to upper left corner
	nFirstChar = CharFromPos(CPoint(0, 0));

	// Get character index close to lower right corner
	nLastChar = CharFromPos(CPoint(rect.right, rect.bottom));
	if (nLastChar < 0)
	{
		nLastChar = GetTextLength();
	}

	// Convert to lines
	nFirstLine = LineFromChar(nFirstChar);
	nLastLine = LineFromChar(nLastChar);

	return (nLastLine - nFirstLine);
}
// 当我把程序最小化,然后添加一行到CRichEdit的时候, 再次打开程序界面发现CRichEdit显示的是空白, 而不是最后一行
// https://www.debugease.com/vc/3471685.html



/*
https://www.cnblogs.com/weiqubo/archive/2010/12/20/1910961.html
(27条消息) C++ 2.0版本的富文本RICHEDIT20A的一些总结_dvlinker的博客-CSDN博客
https://blog.csdn.net/chenlycly/article/details/7678696?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-7678696-blog-121416919.pc_relevant_multi_platform_featuressortv2dupreplace&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-7678696-blog-121416919.pc_relevant_multi_platform_featuressortv2dupreplace&utm_relevant_index=3
*/

//void CLogRichEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
//	// 默认处理
//	CRichEditCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
//	if (mLVI->IsKeyDown) {
//		// 判断是否是左箭头键按下
//		if (nChar == VK_LEFT) {
//			// 获取当前光标位置
//			long nStartChar, nEndChar;
//			// CHARRANGE cr;
//			GetSel(nStartChar, nEndChar);
//			MLOGD("nStartChar %d %d", nStartChar, nEndChar);
//			// 如果光标已在最左边，不做任何处理
//			if (nStartChar == 0 && nEndChar == 0) {
//				return;
//			}
//			CString t16;
//			GetWindowText(t16);// 获取普通文本
//			// 如果你需要获取RTF格式的文本
//			uint32_t dwLength = GetWindowTextLength() + 1;
//			HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwLength * sizeof(TCHAR));
//			if (hGlobal != NULL) {
//				LPTSTR lpBuffer = (LPTSTR)GlobalLock(hGlobal);
//				if (lpBuffer != NULL) {
//					GetWindowText(lpBuffer, dwLength);
//					// 将分配的全局内存对象复制到CString
//					GlobalUnlock(hGlobal);
//				}
//				GlobalFree(hGlobal);
//			}
//			MLOGE("t16 %d %d", t16.GetLength(), dwLength);
//			// 移动光标到右边
//			SetSel(nStartChar + 1, nEndChar + 1);
//		}
//	}
//}

//int CPP AppendAndScroll(const CString& csMsg, COLORREF color, int iFontSize) {
//	long nVisible = 0;
//	long nInsertionPoint = 0;
//	CHARFORMAT cf;
//	GetSelectionCharFormat(cf);
//	// Initialize character format structure
//	cf.cbSize = sizeof(CHARFORMAT);
//	static int iHeight = iFontSize;// 100;
//	cf.dwMask |= CFM_BOLD;
//	cf.dwEffects |= CFE_BOLD;//设置粗体，取消用cf.dwEffects&=~CFE_BOLD;
//	cf.dwEffects &= ~CFE_AUTOCOLOR;
//	cf.dwMask |= CFM_COLOR;
//	cf.crTextColor = color;
//	cf.dwMask |= CFM_SIZE;
//	cf.yHeight = iHeight;//设置高度
//	cf.dwMask |= CFM_FACE;
//	//_tcscpy(cf.szFaceName, _T("微软雅黑"));//设置字体
//	// Set insertion point to end of text
//	nInsertionPoint = GetWindowTextLength();
//	int line = GetLineCount();
//	SetSel(nInsertionPoint, -1);
//	
//	//FLOGE(LOG_BASE, "text ERR B2:%u", GetLastError());
//	// Set the character format
//	SetSelectionCharFormat(cf);
//	//FLOGE(LOG_BASE, "text ERR B1:%u", GetLastError());
//	// Replace selection. Because we have nothing
//	// selected, this will simply insert
//	// the string at the current caret position.
//	ReplaceSel(csMsg);
//	//FLOGE(LOG_BASE, "text ERR B:%u", GetLastError());
//	return 0;
//}

/* {FFF}字符串
{FFF} 为RGB颜色的高4位

*/
//void CLogRichEdit::SetText(char* text) {					// 设置底部的文字 用于提示输入
//	int iFontSize = 222; // 222
//	COLORREF color = 0xff;
//	int length = TxtStrLen(text);
//	int left = TxtFindLeft(text, length, '{');
//	if (left >= 0) {
//		int offset = left + 1;
//		char str[1024] = { 0 };
//		int right;
//		uint8_t r = 0, g = 0, b = 0;
//		do {
//			right = TxtFindLeft(text + offset, length - offset, '}');
//			if (right != -1) { // 有 表示完整
//				r = (uint8_t)TxtHexToValue(text + offset, 1);
//				g = (uint8_t)TxtHexToValue(text + offset + 1, 1);
//				b = (uint8_t)TxtHexToValue(text + offset + 2, 1);
//				color = RGB(r << 4, g << 4, b << 4);
//
//				left = TxtFindLeft(text + offset + right + 1, length - offset - right - 1, '{');
//				if (left != -1) { // 有 表示有下一个
//					memcpy(str, text + offset + right + 1, left);
//					str[left] = 0;
//				}
//				else {
//					memcpy(str, text + offset + right + 1, length - right);
//					str[length - right] = 0;
//				}
//				WCHAR wMsg[4096];
//				TxtGbkUnicode((char*)wMsg, sizeof(wMsg), str);
//				AppendAndScroll(wMsg, color, iFontSize);
//			}
//			else break;
//			offset += right + 1 + left + 1; // offset + {rgb} + 字符串长度 + 1
//
//		} while (1);
//	}
//}